home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 008 / mapovlay.arc / MAP.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1986-05-23  |  21.4 KB  |  649 lines

  1. program Map;
  2.  
  3. { Version 1.7 -- Fixed bug with detection of FORWARD declared routines
  4.           1.6 -- Fixed the detection of overlayed, string-returning, functions under DOS
  5.                  Fixed problem with include directives "hidden" within a comment section
  6.                  Added logic for paged output to the console
  7.                  Added ability to sort output by size or name within overlay group
  8.           1.5 -- Modified the include filename parsing logic
  9.           1.4 -- Modified to put version and machine specific information into an include file
  10.                  Also simplified the stategy to not try looking ahead if overlays end on a sector boundary
  11.           1.3 -- Allows for re-trying an overlay group for different
  12.                  combinations of handling an ambiguous end of overlay
  13.                  situation. }
  14.  
  15. const RevisionDate = 'May 1986';
  16.       RevisionNumber = '1.7';
  17.  
  18.       MaxRoutinesInGroup = 100;                  { Maximum number of routines allowed in any one overlay group }
  19.  
  20. type BinaryFile = file;
  21.      ChrStr = string[127];
  22.      TextFile = text;
  23.      Word = string[127];
  24.  
  25. {$I MAPDOS.PAS }   { <----------  Change this include to configure for various machine configurations }
  26.  
  27. var CurrentWord: Word;
  28.     FileName: ChrStr;
  29.     I: integer;
  30.     IncludeFile: TextFile;
  31.     IncludeFlag: boolean;
  32.     InputFile: TextFile;
  33.     LastChar: char;
  34.     LineCount: integer;
  35.     MainFileName: ChrStr;
  36.     OutputFile: TextFile;
  37.     OutputFileName: ChrStr;
  38.     OverlayGroupNumber: integer;
  39.     OverlayGroupSize: array[0..99] of integer;
  40.     PageLength: integer;
  41.     PageMargin: integer;
  42.     ReUseNextWord: boolean;
  43.     SortMode: integer;
  44.  
  45. procedure ScanForOverlayGroup; forward;
  46.  
  47. procedure Terminate(ErrorString: ChrStr);
  48.   { Output an error message and halt }
  49.   begin
  50.   writeln;
  51.   writeln(ErrorString);
  52.   halt
  53.   end;
  54.  
  55. procedure BumpLineCount;
  56.   { Increment the line count and display every 16 lines to emulate the compiler }
  57.   begin
  58.   LineCount := succ(LineCount);
  59.   if (LineCount and $000F)=0 then                { Faster than (LineCount mod 16)=0 }
  60.     if IncludeFlag
  61.      then
  62.       write(^M'I ',LineCount)
  63.      else
  64.       write(^M'  ',LineCount)
  65.   end;
  66.  
  67. function CompareWord(var Master: Word;Template: Word): boolean;
  68.   { Return true if template matches the Master (case is ignored) }
  69.   var Equal: boolean;
  70.       I: integer;
  71.   begin
  72.   if length(Master)<>length(Template)
  73.    then
  74.     CompareWord := false
  75.    else
  76.     begin
  77.     Equal := true;
  78.     I := 1;
  79.     while Equal and (I<=length(Master)) do
  80.       begin
  81.       Equal := upcase(Master[I])=Template[I];
  82.       I := succ(I)
  83.       end;
  84.     CompareWord := Equal
  85.     end
  86.   end;
  87.  
  88. function OverlayFileName(OverlayNumber: integer): ChrStr;
  89.   { Return the name of the overlay for the given overlay group }
  90.   var Extension: string[3];
  91.   begin
  92.   str(OverlayNumber,Extension);
  93.   Extension := copy('00'+Extension,length(Extension),3);
  94.   OverlayFileName := copy(MainFileName,1,pos('.',MainFileName))+Extension
  95.   end;
  96.  
  97. function TempFileName(OverlayNumber: integer): ChrStr;
  98.   { Return the name of the temporary file for the given overlay group }
  99.   var Extension: string[3];
  100.   begin
  101.   str(OverlayNumber,Extension);
  102.   Extension := copy('0'+Extension,length(Extension),2);
  103.   TempFileName := copy(MainFileName,1,pos('.',MainFileName))+'$'+Extension
  104.   end;
  105.  
  106. function GetNextWord(var NextWord: Word): boolean;
  107.   { Return next word from input file and set result false on EOF }
  108.   var EndOfWord: boolean;
  109.       InputState: (Normal,BraceComment,BraceDirective,ParenComment,ParenDirective,StringConstant);
  110.       NextChar: char;
  111.  
  112.   procedure HandleIncludeDirective;
  113.     { Redirect input because of an include directive }
  114.     const ValidFileChars: set of char = ['A'..'Z','a'..'z','0'..'9','.','\'];
  115.     var I: integer;
  116.     begin
  117.     if not IncludeFlag then
  118.       begin
  119.       if seekeoln(InputFile) then ;              { Skip any leading spaces }
  120.       BumpLineCount;                             { So we don't lose the carriage return }
  121.       readln(InputFile,FileName);
  122.       if not (FileName[1] in ['+','-']) then     { Skip I/O error checking directives }
  123.         begin
  124.         I := 0;
  125.         while FileName[succ(I)] in ValidFileChars do
  126.           I := succ(I);                          { Keep only valid filename characters }
  127.         FileName := copy(FileName,1,I);
  128.         if pos('.',FileName)=0 then
  129.           FileName := FileName+'.PAS';           { Default extension to .PAS }
  130.         assign(IncludeFile,FileName);
  131.         {$I-}
  132.         reset(IncludeFile);
  133.         {$I+}
  134.         if ioresult<>0 then
  135.           Terminate(^G'Include file ('+FileName+') not found.');
  136.         EndOfWord := true;
  137.         IncludeFlag := true
  138.         end
  139.       end
  140.     end;
  141.  
  142.   begin
  143.   GetNextWord := true;
  144.   if ReUseNextWord
  145.    then
  146.     ReUseNextWord := false
  147.    else
  148.     begin
  149.     NextWord := '';
  150.     repeat
  151.       EndOfWord := false;
  152.       InputState := Normal;
  153.       repeat
  154.         if IncludeFlag
  155.          then
  156.           if eof(IncludeFile)
  157.            then
  158.             begin
  159.             EndOfWord := true;
  160.             IncludeFlag := false;
  161.             close(IncludeFile)
  162.             end
  163.            else
  164.             read(IncludeFile,NextChar)
  165.          else
  166.           if eof(InputFile)
  167.            then
  168.             begin
  169.             EndOfWord := true;
  170.             GetNextWord := false
  171.             end
  172.            else
  173.             read(InputFile,NextChar);
  174.         if NextChar=#13 then                     { Count the number of lines processed so far }
  175.           BumpLineCount;
  176.         if not EndOfWord then
  177.           case InputState of
  178.             Normal: case NextChar of
  179.                       '{': InputState := BraceDirective;
  180.                       '*': if LastChar='(' then
  181.                              InputState := ParenDirective;
  182.                       '''': InputState := StringConstant;
  183.                       'a'..'z','A'..'Z','_','0'..'9': NextWord := NextWord+NextChar
  184.                       else EndOfWord := NextWord<>''
  185.                       end;
  186.             BraceComment: EndOfWord := NextChar='}';  { Wait for the trailing comment mark }
  187.             BraceDirective: begin
  188.                             EndOfWord := NextChar='}';  { In case of a pair of braces adjacent }
  189.                             if not EndOfWord and (LastChar+NextChar <> '{$') then
  190.                               begin
  191.                               if LastChar+NextChar = '$I' then
  192.                                 HandleIncludeDirective;
  193.                               InputState := BraceComment  { An include directive no longer possible }
  194.                               end
  195.                             end;
  196.             ParenComment: EndOfWord := LastChar+NextChar='*)';  { Wait for the trailing comment mark }
  197.             ParenDirective: if LastChar+NextChar <> '*$' then
  198.                               begin
  199.                               if LastChar+NextChar = '$I' then
  200.                                 HandleIncludeDirective;
  201.                               InputState := ParenComment  { An include directive no longer possible }
  202.                               end;
  203.             StringConstant: if NextChar='''' then
  204.                               EndOfWord := true
  205.             end;
  206.         LastChar := NextChar
  207.       until EndOfWord
  208.     until (NextWord<>'') or eof(InputFile)
  209.     end
  210.   end;
  211.  
  212. procedure RetryOverlayGroup(CurrentOverlayNumber: integer;var OverlayFyle: BinaryFile;var DescriptionFyle: TextFile);
  213.   { If the first try at analyzing the overlay group didn't work, then we'll try
  214.     again and handle the ambiguous end of overlay detection differently until
  215.     we get a pattern which works }
  216.   var AmbiguousCount: integer;
  217.       BufferIndex: integer;
  218.       ErrorDetected: boolean;
  219.       NewDescriptionFyle: TextFile;
  220.       RetryCount: integer;
  221.       RoutineType: char;
  222.       RoutineName: Word;
  223.       SizeInBytes: integer;
  224.   begin
  225.   writeln;
  226.   RetryCount := 1;                               { Use this integer as a binary pattern }
  227.   repeat
  228.     writeln('Retry number ',RetryCount,' on overlay group ',CurrentOverlayNumber,'.');
  229.     reset(OverlayFyle);
  230.     reset(DescriptionFyle);
  231.     assign(NewDescriptionFyle,copy(MainFileName,1,pos('.',MainFileName))+'$$$');
  232.     rewrite(NewDescriptionFyle);
  233.     OverlayGroupSize[CurrentOverlayNumber] := 0;
  234.     AmbiguousCount := 0;
  235.     ErrorDetected := false;
  236.     while not eof(DescriptionFyle) and not ErrorDetected do
  237.       begin
  238.       readln(DescriptionFyle,RoutineType,SizeInBytes,RoutineName);
  239.  
  240.       SizeInBytes := -QuantizationSize;
  241.       repeat
  242.         SizeInBytes := SizeInBytes+QuantizationSize;
  243.         {$I-}
  244.         blockread(OverlayFyle,Buffer,QuantizationSize div 128);
  245.         {$I+}
  246.         ErrorDetected := ioresult<>0             { Underflow in the overlay file }
  247.       until EndOfOverlay(OverlayFyle,BufferIndex,AmbiguousCount,RetryCount) or ErrorDetected;
  248.       SizeInBytes := SizeInBytes+BufferIndex;    { BufferIndex is the number of code bytes in last record of overlay }
  249.       if OverlayGroupSize[CurrentOverlayNumber]*QuantizationSize < SizeInBytes then
  250.         OverlayGroupSize[CurrentOverlayNumber] := (SizeInBytes+QuantizationSize-1) div QuantizationSize;
  251.  
  252.       writeln(NewDescriptionFyle,RoutineType,' ',SizeInBytes,' ',RoutineName)
  253.       end;
  254.     ErrorDetected := not eof(OverlayFyle);
  255.     close(OverlayFyle);
  256.     close(DescriptionFyle);
  257.     close(NewDescriptionFyle);
  258.     RetryCount := succ(RetryCount)
  259.   until not ErrorDetected or (RetryCount>=round(exp(ln(2.0)*AmbiguousCount)));
  260.   if not ErrorDetected
  261.    then
  262.     begin
  263.     erase(DescriptionFyle);
  264.     rename(NewDescriptionFyle,TempFileName(CurrentOverlayNumber))
  265.     end
  266.    else
  267.     Terminate(^G'Overlay file cannot be analyzed.')
  268.   end;
  269.  
  270. {$A-}
  271. procedure ScanForEnd;
  272.   { Scan the source code looking for the matching END for the current keyword }
  273.   var NotMatchingRecord: boolean;
  274.   begin
  275.   NotMatchingRecord := not CompareWord(CurrentWord,'RECORD');
  276.   while GetNextWord(CurrentWord) do
  277.     if CompareWord(CurrentWord,'END')
  278.      then
  279.       exit
  280.      else
  281.       if CompareWord(CurrentWord,'BEGIN') or CompareWord(CurrentWord,'RECORD') or
  282.          (NotMatchingRecord and CompareWord(CurrentWord,'CASE')) then
  283.         ScanForEnd
  284.   end;
  285.  
  286. procedure ScanForRoutine;
  287.   { Scan the source code looking for the end the current routine (procedure or function) }
  288.   begin
  289.   while GetNextWord(CurrentWord) do
  290.     if CompareWord(CurrentWord,'BEGIN')
  291.      then
  292.       begin
  293.       ScanForEnd;
  294.       exit
  295.       end
  296.      else
  297.       if CompareWord(CurrentWord,'FORWARD')
  298.        then
  299.         exit                                     { Once you see the FORWARD, you're done }
  300.        else
  301.         if CompareWord(CurrentWord,'RECORD') or CompareWord(CurrentWord,'CASE')
  302.          then
  303.           ScanForEnd
  304.          else
  305.           if CompareWord(CurrentWord,'PROCEDURE') or CompareWord(CurrentWord,'FUNCTION')
  306.            then
  307.             ScanForRoutine
  308.            else
  309.             if CompareWord(CurrentWord,'OVERLAY') then
  310.               ScanForOverlayGroup
  311.   end;
  312.  
  313. procedure ScanForOverlay(CurrentOverlayNumber: integer;var Fyle: BinaryFile;var TempFyle: TextFile);
  314.   { Scan for the end of the current overlay routine (procedure or function) }
  315.   var AmbiguousCount: integer;
  316.       BufferIndex: integer;
  317.       SizeInBytes: integer;
  318.   begin
  319.   AmbiguousCount := 0;
  320.   if GetNextWord(CurrentWord) then ;             { Get the following PROCEDURE or FUNCTION }
  321.   write(TempFyle,upcase(CurrentWord[1]),' ');
  322.   if GetNextWord(CurrentWord) then ;             { Get name of procedure or function }
  323.  
  324.   SizeInBytes := -QuantizationSize;
  325.   repeat
  326.     SizeInBytes := SizeInBytes+QuantizationSize;
  327.     {$I-}
  328.     blockread(Fyle,Buffer,QuantizationSize div 128);
  329.     {$I+}
  330.     if ioresult<>0 then                          { The data in the overlay file has fooled us }
  331.       Terminate(^G'Overlay file cannot be analyzed (underflow).')
  332.   until EndOfOverlay(Fyle,BufferIndex,AmbiguousCount,0);
  333.   SizeInBytes := SizeInBytes+BufferIndex;        { BufferIndex is the number of code bytes in last record of overlay }
  334.   writeln(TempFyle,SizeInBytes,' ',CurrentWord);
  335.   if OverlayGroupSize[CurrentOverlayNumber]*QuantizationSize < SizeInBytes then
  336.     OverlayGroupSize[CurrentOverlayNumber] := (SizeInBytes+QuantizationSize-1) div QuantizationSize;
  337.  
  338.   ScanForRoutine
  339.   end;
  340.  
  341. procedure ScanForOG(CurrentOverlayNumber: integer);
  342.   { Scan for the end of the current overlay group }
  343.   var ErrorDetected: boolean;
  344.       Fyle: BinaryFile;
  345.       TempFyle: TextFile;
  346.   begin
  347.   assign(Fyle,OverlayFileName(CurrentOverlayNumber));
  348.   {$I-}
  349.   reset(Fyle);
  350.   {$I+}
  351.   if ioresult<>0 then
  352.     Terminate(^G'Overlay file not found.');
  353.  
  354.   assign(TempFyle,TempFileName(CurrentOverlayNumber));
  355.   {$I-}
  356.   rewrite(TempFyle);
  357.   {$I+}
  358.   if ioresult<>0 then
  359.     Terminate(^G'Directory full.');
  360.  
  361.   ScanForOverlay(CurrentOverlayNumber,Fyle,TempFyle);
  362.   while GetNextWord(CurrentWord) do
  363.     if CompareWord(CurrentWord,'OVERLAY')
  364.      then
  365.       ScanForOverlay(CurrentOverlayNumber,Fyle,TempFyle)
  366.      else
  367.       begin
  368.       ReUseNextWord := true;
  369.       ErrorDetected := not eof(Fyle);
  370.       close(Fyle);
  371.       close(TempFyle);
  372.       if ErrorDetected then
  373.         RetryOverlayGroup(CurrentOverlayNumber,Fyle,TempFyle);
  374.       exit
  375.       end
  376.   end;
  377. {$A+}
  378.  
  379. procedure ScanForOverlayGroup;                   { Note the FORWARD declaration above }
  380.   { Call the recursive routine to find the end of the current overlay group }
  381.   begin
  382.   OverlayGroupNumber := succ(OverlayGroupNumber);
  383.   ScanForOG(OverlayGroupNumber)
  384.   end;
  385.  
  386. procedure EnterParameters;
  387.   { Get the filenames and page layout values either from the command line or by prompting }
  388.   var Ch: char;
  389.       ErrorCode: integer;
  390.   begin
  391.   if paramcount>=1
  392.    then
  393.     MainFileName := paramstr(1)
  394.    else
  395.     repeat
  396.       write('Enter name of source code file:');
  397.       readln(MainFileName)
  398.     until MainFileName<>'';
  399.   MainFileName := MainFileName+'.PAS';
  400.   assign(InputFile,MainFileName);
  401.   {$I-}
  402.   reset(InputFile);
  403.   {$I+}
  404.   if ioresult<>0 then
  405.     Terminate(^G'File not found.');
  406.  
  407.   if paramcount>=2
  408.    then
  409.     OutputFileName := paramstr(2)
  410.    else
  411.     begin
  412.     write('Enter name of result file (default is the console):');
  413.     readln(OutputFileName);
  414.     for ErrorCode := 1 to length(OutputFileName) do
  415.       OutputFileName[ErrorCode] := upcase(OutputFileName[ErrorCode]);
  416.     if OutputFileName='' then
  417.       OutputFileName := 'CON:'
  418.     end;
  419.   assign(OutputFile,OutputFileName);
  420.   {$I-}
  421.   rewrite(OutputFile);
  422.   {$I+}
  423.   if ioresult<>0 then
  424.     Terminate(^G'Directory full.');
  425.  
  426.  
  427.   if paramcount>=3
  428.    then
  429.     Ch := upcase(copy(paramstr(3),1,1))
  430.    else
  431.     if paramcount>=1
  432.      then
  433.       Ch := 'N'                                  { Default to no sort not mentioned on command line }
  434.      else
  435.       begin
  436.       write('Sort the output (Y/N)? ');
  437.       repeat
  438.         read(kbd,Ch);
  439.         Ch := upcase(Ch)
  440.       until Ch in ['Y','N'];
  441.       writeln(Ch);
  442.       if Ch='Y' then
  443.         begin
  444.         write('  Sort Alphabetically or by Size (A/S)? ');
  445.         repeat
  446.           read(kbd,Ch);
  447.           Ch := upcase(Ch)
  448.         until Ch in ['A','S'];
  449.         writeln(Ch)
  450.         end
  451.       end;
  452.   if Ch='A'                                      { Sort mode character - (N)o sort, sort by (S)ize, sort (A)lphabetically) }
  453.    then
  454.     SortMode := 1
  455.    else
  456.     if Ch='S'
  457.      then
  458.       SortMode := 2
  459.      else
  460.       SortMode := 0;                             { Default to no sorting }
  461.  
  462.   if OutputFileName='CON:'
  463.    then
  464.     PageLength := 24                             { Default for 24 or 25 line displays }
  465.    else
  466.     PageLength := 66;                            { Default page length }
  467.   if paramcount>=4 then
  468.     begin
  469.     val(paramstr(4),PageLength,ErrorCode);
  470.     if (ErrorCode<>0) or (PageLength<8) then
  471.       Terminate(^G'Invalid page length.')
  472.     end;
  473.  
  474.   if OutputFileName='CON:'
  475.    then
  476.     PageMargin := 1                              { Default for 24 or 25 line displays }
  477.    else
  478.     PageMargin := 6;                             { Default margin setting }
  479.   if paramcount>=5 then
  480.     begin
  481.     val(paramstr(5),PageMargin,ErrorCode);
  482.     if (ErrorCode<>0) or (PageLength < 2*PageMargin+6) or (PageMargin<1) then
  483.       Terminate(^G'Invalid margin setting.')
  484.     end;
  485.   writeln
  486.   end;
  487.  
  488. procedure OutputDataCollected;
  489.   { Display the data }
  490.   type DataType = record
  491.          Name: Word;
  492.          Size: integer;
  493.          ProcFunc: char
  494.          end;
  495.   var Ch: char;
  496.       Data: array[1..MaxRoutinesInGroup] of DataType;
  497.       I: integer;
  498.       J: integer;
  499.       LineNumber: integer;
  500.       RoutineNum: integer;
  501.  
  502.   function LessThan(var A,B: DataType): boolean;
  503.     { Return true iff A < B for DataType }
  504.     begin
  505.     if SortMode = 1
  506.      then
  507.       LessThan := A.Name < B.Name
  508.      else
  509.       LessThan := A.Size > B.Size
  510.     end;
  511.  
  512.   {$A-}
  513.   procedure QuickSort(First,Last: integer);
  514.     { A simple quicksort routine to sort the output information }
  515.     { Thanks to Ira Polans 74065,403 for the algorithm }
  516.     var Upper, Lower: integer;
  517.         Pivot: DataType;
  518.  
  519.     procedure Exchange(var A,B: DataType);
  520.       { Exchange the array elements }
  521.       var Temp: DataType;
  522.       begin
  523.       Temp := A;
  524.       A := B;
  525.       B := Temp
  526.       end;
  527.  
  528.     begin
  529.     if First < Last then
  530.       begin
  531.       Upper := First;
  532.       Lower := Last;
  533.       Pivot := Data[Last];
  534.       repeat
  535.         while LessThan(Data[Upper],Pivot) do
  536.           Upper := succ(Upper);
  537.         while not LessThan(Data[Lower],Pivot) and (Lower > Upper) do
  538.           Lower := pred(Lower);
  539.         if Upper < Lower then
  540.           Exchange(Data[Upper],Data[Lower])
  541.       until Upper = Lower;
  542.       Exchange(Data[Upper],Data[Last]);          { Move pivot value to partion the group }
  543.       QuickSort(First,pred(Upper));              { Sort the top group }
  544.       QuickSort(succ(Upper),Last)                { Sort the bottom group }
  545.       end
  546.     end;
  547.   {$A+}
  548.  
  549.   procedure CheckEndOfPage;
  550.     { Test if it is time to go to the next page }
  551.     var I: integer;
  552.     begin
  553.     if LineNumber+3 > succ(PageLength-PageMargin) then
  554.       begin
  555.       for I := LineNumber to pred(PageLength+PageMargin) do
  556.         writeln(OutputFile);
  557.       if OutputFileName='CON:' then
  558.         begin
  559.         write('                      --  Hit any key to continue  --');
  560.         read(kbd,Ch);
  561.         writeln
  562.         end;
  563.       LineNumber := PageMargin
  564.       end
  565.     end;
  566.  
  567.   begin
  568.   if OutputFileName<>'CON:' then
  569.     writeln('Generating report...');
  570.   for I := 0 to OverlayGroupNumber do
  571.     begin
  572.     assign(InputFile,TempFileName(I));
  573.     reset(InputFile);
  574.     RoutineNum := 0;
  575.     while not eof(InputFile) and (RoutineNum<MaxRoutinesInGroup) do  { Read in overlay group data }
  576.       begin
  577.       RoutineNum := succ(RoutineNum);
  578.       readln(InputFile,Data[RoutineNum].ProcFunc,Data[RoutineNum].Size,Data[RoutineNum].Name)
  579.       end;
  580.     close(InputFile);
  581.     if SortMode<>0 then                          { Optionally sort the output data }
  582.       QuickSort(1,RoutineNum);
  583.  
  584.     for J := 1 to PageMargin do
  585.       writeln(OutputFile);
  586.     writeln(OutputFile,'                                       Length in  Length in  Bytes to  Capacity');
  587.     writeln(OutputFile,'                                         bytes     records     spare     used  ');
  588.     writeln(OutputFile,'                                       ---------  ---------  --------  --------');
  589.     writeln(OutputFile,'OVERLAY GROUP ',I:2,1.0*QuantizationSize*OverlayGroupSize[I]:30:0,OverlayGroupSize[I]:10);
  590.     LineNumber := PageMargin + 5;
  591.     for RoutineNum := 1 to RoutineNum do
  592.       begin
  593.       CheckEndOfPage;
  594.       writeln(OutputFile);
  595.       if Data[RoutineNum].ProcFunc='F'
  596.        then
  597.         write(OutputFile,'function',Data[RoutineNum].Name,'':31-length(Data[RoutineNum].Name))
  598.        else
  599.         write(OutputFile,'procedure',Data[RoutineNum].Name,'':30-length(Data[RoutineNum].Name));
  600.       writeln(OutputFile,Data[RoutineNum].Size:7,(Data[RoutineNum].Size+QuantizationSize-1) div QuantizationSize:10,
  601.                          1.0*QuantizationSize*OverlayGroupSize[I]-Data[RoutineNum].Size:11:0,
  602.                          100.0*Data[RoutineNum].Size/(1.0*QuantizationSize*OverlayGroupSize[I]):10:1,' %');
  603.       for J := 1 to round(79.0*Data[RoutineNum].Size/(1.0*QuantizationSize*OverlayGroupSize[I])) do
  604.         write(OutputFile,'X');
  605.       writeln(OutputFile);
  606.       LineNumber := LineNumber+3
  607.       end;
  608.     for J := LineNumber to PageLength do
  609.       writeln(OutputFile);
  610.     if (OutputFileName='CON:') and (I<>OverlayGroupNumber) then
  611.       begin
  612.       write('                      --  Hit any key to continue  --');
  613.       read(kbd,Ch);
  614.       writeln
  615.       end;
  616.  
  617.     erase(InputFile)
  618.     end;
  619.   close(OutputFile)
  620.   end;
  621.  
  622. begin
  623. writeln('Overlay mapper version ',RevisionNumber,' for ',MachineType,' Turbo Pascal.');
  624. writeln('  by Scott Bussinger -- ',RevisionDate);
  625. writeln;
  626.  
  627. EnterParameters;
  628.  
  629. IncludeFlag := false;
  630. LastChar := ' ';
  631. OverlayGroupNumber := -1;
  632. for I := 0 to 99 do
  633.   OverlayGroupSize[I] := 0;
  634. ReUseNextWord := false;
  635.  
  636. writeln('Analyzing');
  637. LineCount := -1;
  638. BumpLineCount;
  639.  
  640. while GetNextWord(CurrentWord) do
  641.   if CompareWord(CurrentWord,'OVERLAY') then
  642.     ScanForOverlayGroup;
  643. close(InputFile);
  644.  
  645. writeln(^M'  ',LineCount);
  646. OutputDataCollected;
  647. write(^G'Analysis complete.')
  648. end.
  649.